package cloud.shyidong.core.service;

import cloud.shyidong.core.constant.DeleteConstant;
import cloud.shyidong.core.constant.StatusConstant;
import cloud.shyidong.core.context.Context;
import cloud.shyidong.core.context.ContextHolder;
import cloud.shyidong.core.dao.BaseDao;
import cloud.shyidong.core.entity.BaseEntity;
import cloud.shyidong.core.exception.BusinessException;
import cloud.shyidong.core.request.*;
import cloud.shyidong.core.response.PageResponse;
import cloud.shyidong.core.response.SimplePageResponse;
import cloud.shyidong.core.util.ObjectUtils;
import cloud.shyidong.core.util.SpringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.dozermapper.core.Mapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description TODO
 * @Author syd
 * @Date 2023/3/27
 */
@Slf4j
public class AbstractService<D extends BaseDao, T extends BaseEntity, ID extends IdRequest, IQ extends InsertRequest, UQ extends UpdateRequest, PQ extends PageRequest> implements BaseService<D, T, ID, IQ, UQ, PQ> {
    
    protected Mapper mapper;
    
    private D dao;
    
    protected D getDao() {
        if (ObjectUtils.isNull(dao)) {
            Type type = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
            this.dao = (D) SpringUtils.getBean((Class) type);
        }
        return dao;
    }
    
    @Autowired
    public void setMapper(Mapper mapper) {
        this.mapper = mapper;
    }
    
    @Override
    public void beforeInsert(IQ insertRequest) {
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public T insert(IQ insertRequest) {
        Context context = ContextHolder.getContext();
        if (ObjectUtils.isNull(context)) {
            throw BusinessException.from("此地址需要认证后才能访问");
        }
        this.beforeInsert(insertRequest);
        Type type = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
        T t = (T) mapper.map(insertRequest, (Class) type);
        this.processInsert(insertRequest, t);
        Class clazz = t.getClass();
        try {
            clazz.getDeclaredMethod("setCreateUser", Long.class).invoke(t, context.getUserId());
            clazz.getDeclaredMethod("setCreateTime", Date.class).invoke(t, new Date());
            clazz.getDeclaredMethod("setUpdateUser", Long.class).invoke(t, context.getUserId());
            clazz.getDeclaredMethod("setUpdateTime", Date.class).invoke(t, new Date());
            clazz.getDeclaredMethod("setDeleted", Integer.class).invoke(t, DeleteConstant.NOT_DELETED);
            if (getDao().insert(t) == 0) {
                throw BusinessException.from("新增失败");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw BusinessException.from(e.getMessage());
        }
        this.afterInsert(insertRequest, t);
        return t;
    }
    
    @Override
    public void processInsert(IQ insertRequest, T t) {
    
    }
    
    @Override
    public void afterInsert(IQ insertRequest, T t) {
    }
    
    @Override
    public void beforeUpdate(UQ updateRequest) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public T update(UQ updateRequest) {
        try {
            Context context = ContextHolder.getContext();
            if (ObjectUtils.isNull(context)) {
                throw BusinessException.from("此地址需要认证后才能访问");
            }
            Type updateRequestType = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[4];
            Class updateRequestClass = (Class) updateRequestType;
            Serializable serializable = (Serializable) updateRequestClass.getDeclaredMethod("getId").invoke(updateRequest);
            T t = (T) getDao().selectById(serializable);
            if (ObjectUtils.isNull(t)) {
                throw BusinessException.from("修改的数据不存在");
            }
            Class clazz = t.getClass();
            this.beforeUpdate(updateRequest);
            Type type = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
            t = (T) mapper.map(updateRequest, (Class) type);
            this.processUpdate(updateRequest, t);
            clazz.getDeclaredMethod("setUpdateUser", Long.class).invoke(t, context.getUserId());
            clazz.getDeclaredMethod("setUpdateTime", Date.class).invoke(t, new Date());
            if (getDao().updateById(t) == 0) {
                throw BusinessException.from("更新失败");
            }
            this.afterUpdate(updateRequest, t);
            return t;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw BusinessException.from(e.getMessage());
        }
    }
    
    @Override
    public void processUpdate(UQ updateRequest, T t) {
    
    }
    
    @Override
    public void afterUpdate(UQ updateRequest, T t) {
    
    }
    
    @Override
    public void beforeDelete(ID idRequest) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public T delete(ID idRequest) {
        try {
            T t = (T) getDao().selectById(idRequest.getId());
            if (ObjectUtils.isNull(t)) {
                throw BusinessException.from("未找到要删除的记录");
            }
            Class clazz = t.getClass();
            Integer status = (Integer) clazz.getDeclaredMethod("getStatus").invoke(t);
            if (ObjectUtils.isNull(status) || status.intValue() > StatusConstant.ENABLE) {
                throw BusinessException.from("删除的数据的状态不存在，或此状态不可删除");
            }
            this.beforeDelete(idRequest);
            if (getDao().deleteById(idRequest.getId()) == 0) {
                throw BusinessException.from("删除失败");
            }
            this.afterDelete(idRequest, t);
            return t;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw BusinessException.from(e.getMessage());
        }
    }
    
    @Override
    public void afterDelete(ID idRequest, T t) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> deletes(IdsRequest<ID> idsRequest) {
        Integer count = 0;
        for (ID id : idsRequest.getIds()) {
            T t = (T) getDao().selectById(id.getId());
            if (ObjectUtils.isNull(t)) {
                continue;
            }
            if (getDao().deleteById(id.getId()) > 0) {
                count++;
            }
        }
        if (count.intValue() == 0) {
            throw BusinessException.from("批量删除失败");
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("count", count);
        return result;
    }
    
    @Override
    public void beforeEnable(ID idRequest) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public T enable(ID idRequest) {
        this.beforeEnable(idRequest);
        T t = (T) getDao().selectById(idRequest.getId());
        if (ObjectUtils.isNull(t)) {
            throw BusinessException.from("未找到要启用的记录");
        }
        try {
            Integer status = (Integer) t.getClass().getDeclaredMethod("getStatus").invoke(t);
            if (ObjectUtils.isNotNull(status) && status.intValue() != StatusConstant.DISABLE) {
                throw BusinessException.from("已经启用，启用失败");
            }
            t.getClass().getDeclaredMethod("setStatus", Integer.class).invoke(t, StatusConstant.ENABLE);
            if (getDao().updateById(t) == 0) {
                throw BusinessException.from("启用失败");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw BusinessException.from(e.getMessage());
        }
        this.afterEnable(idRequest, t);
        return t;
    }
    
    @Override
    public void afterEnable(ID idRequest, T t) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> enables(IdsRequest<ID> idsRequest) {
        Integer count = 0;
        for (ID id : idsRequest.getIds()) {
            T t = (T) getDao().selectById(id.getId());
            if (ObjectUtils.isNull(t)) {
                throw BusinessException.from("未找到要启用的记录");
            }
            try {
                Integer status = (Integer) t.getClass().getDeclaredMethod("getStatus").invoke(t);
                if (ObjectUtils.isNotNull(status) && status.intValue() != StatusConstant.DISABLE) {
                    continue;
                }
                t.getClass().getDeclaredMethod("setStatus", Integer.class).invoke(t, StatusConstant.ENABLE);
                if (getDao().updateById(t) > 0) {
                    count++;
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw BusinessException.from(e.getMessage());
            }
        }
        if (count.intValue() == 0) {
            throw BusinessException.from("批量启用失败");
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("count", count);
        return result;
    }
    
    @Override
    public void beforeDisable(ID idRequest) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public T disable(ID idRequest) {
        this.beforeDisable(idRequest);
        T t = (T) getDao().selectById(idRequest.getId());
        if (ObjectUtils.isNull(t)) {
            throw BusinessException.from("未找到要禁用的记录");
        }
        try {
            Integer status = (Integer) t.getClass().getDeclaredMethod("getStatus").invoke(t);
            if (ObjectUtils.isNotNull(status) && status.intValue() != StatusConstant.ENABLE) {
                throw BusinessException.from("已经禁用，禁用失败");
            }
            t.getClass().getDeclaredMethod("setStatus", Integer.class).invoke(t, StatusConstant.DISABLE);
            if (getDao().updateById(t) == 0) {
                throw BusinessException.from("禁用失败");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw BusinessException.from(e.getMessage());
        }
        this.afterDisable(idRequest, t);
        return t;
    }
    
    @Override
    public void afterDisable(ID idRequest, T t) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> disables(IdsRequest<ID> idsRequest) {
        Integer count = 0;
        for (ID id : idsRequest.getIds()) {
            T t = (T) getDao().selectById(id.getId());
            if (ObjectUtils.isNull(t)) {
                throw BusinessException.from("未找到要禁用的记录");
            }
            try {
                Integer status = (Integer) t.getClass().getDeclaredMethod("getStatus").invoke(t);
                if (ObjectUtils.isNotNull(status) && status.intValue() != StatusConstant.ENABLE) {
                    continue;
                }
                t.getClass().getDeclaredMethod("setStatus", Integer.class).invoke(t, StatusConstant.DISABLE);
                if (getDao().updateById(t) > 0) {
                    count++;
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw BusinessException.from(e.getMessage());
            }
        }
        if (count.intValue() == 0) {
            throw BusinessException.from("批量禁用失败");
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("count", count);
        return result;
    }
    
    @Override
    public void beforeUp(ID idRequest) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public T up(ID idRequest) {
        this.beforeUp(idRequest);
        T t = (T) getDao().selectById(idRequest.getId());
        if (ObjectUtils.isNull(t)) {
            throw BusinessException.from("未找到要上架的记录");
        }
        try {
            Integer status = (Integer) t.getClass().getDeclaredMethod("getStatus").invoke(t);
            if (ObjectUtils.isNotNull(status) && status.intValue() != StatusConstant.ENABLE) {
                throw BusinessException.from("上架失败");
            }
            t.getClass().getDeclaredMethod("setStatus", Integer.class).invoke(t, StatusConstant.UP);
            if (getDao().updateById(t) == 0) {
                throw BusinessException.from("上架失败");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw BusinessException.from(e.getMessage());
        }
        this.afterUp(idRequest, t);
        return t;
    }
    
    @Override
    public void afterUp(ID idRequest, T t) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> ups(IdsRequest<ID> idsRequest) {
        Integer count = 0;
        for (ID id : idsRequest.getIds()) {
            T t = (T) getDao().selectById(id.getId());
            if (ObjectUtils.isNull(t)) {
                throw BusinessException.from("未找到要上架的记录");
            }
            try {
                Integer status = (Integer) t.getClass().getDeclaredMethod("getStatus").invoke(t);
                if (ObjectUtils.isNotNull(status) && status.intValue() != StatusConstant.ENABLE) {
                    continue;
                }
                t.getClass().getDeclaredMethod("setStatus", Integer.class).invoke(t, StatusConstant.UP);
                if (getDao().updateById(t) > 0) {
                    count++;
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw BusinessException.from(e.getMessage());
            }
        }
        if (count.intValue() == 0) {
            throw BusinessException.from("批量上架失败");
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("count", count);
        return result;
    }
    
    @Override
    public void beforeDown(ID idRequest) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public T down(ID idRequest) {
        this.beforeDown(idRequest);
        T t = (T) getDao().selectById(idRequest.getId());
        if (ObjectUtils.isNull(t)) {
            throw BusinessException.from("未找到要下架的记录");
        }
        try {
            Integer status = (Integer) t.getClass().getDeclaredMethod("getStatus").invoke(t);
            if (ObjectUtils.isNotNull(status) && status.intValue() != StatusConstant.UP) {
                throw BusinessException.from("下架失败");
            }
            t.getClass().getDeclaredMethod("setStatus", Integer.class).invoke(t, StatusConstant.ENABLE);
            if (getDao().updateById(t) == 0) {
                throw BusinessException.from("下架失败");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw BusinessException.from(e.getMessage());
        }
        this.afterDown(idRequest, t);
        return t;
    }
    
    @Override
    public void afterDown(ID idRequest, T t) {
    
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> downs(IdsRequest<ID> idsRequest) {
        Integer count = 0;
        for (ID id : idsRequest.getIds()) {
            T t = (T) getDao().selectById(id.getId());
            if (ObjectUtils.isNull(t)) {
                throw BusinessException.from("未找到要下架的记录");
            }
            try {
                Integer status = (Integer) t.getClass().getDeclaredMethod("getStatus").invoke(t);
                if (ObjectUtils.isNotNull(status) && status.intValue() != StatusConstant.UP) {
                    continue;
                }
                t.getClass().getDeclaredMethod("setStatus", Integer.class).invoke(t, StatusConstant.ENABLE);
                if (getDao().updateById(t) > 0) {
                    count++;
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw BusinessException.from(e.getMessage());
            }
        }
        if (count.intValue() == 0) {
            throw BusinessException.from("批量下架失败");
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("count", count);
        return result;
    }
    
    @Override
    public Map<String, Object> view(ID idRequest) {
        T t = (T) getDao().selectById(idRequest.getId());
        return mapper.map(t, LinkedHashMap.class);
    }
    
    @Override
    public List<T> search(PQ pageRequest) {
        return getDao().selectList(new QueryWrapper<T>().orderByAsc("sort_index"));
    }
    
    @Override
    public PageResponse<Map<String, Object>> page(PQ pageRequest) {
        long count = this.getDao().countBySelective(pageRequest);
        List<Map<String, Object>> list = getDao().selectBySelective(pageRequest);
        return new SimplePageResponse<Map<String, Object>>(count, pageRequest.getSize(), pageRequest.getCurrent()).setRecords(list);
    }
    
}
